using System;
using Infinity.Common.Extensions;

namespace Infinity.Common
{
	/// <summary>
	/// General error class for throwing exceptions
	/// </summary>
	public static class Error
	{
		#region Exception methods

		/// <summary>
		/// It returns a general exception with given message and inner exception.
		/// </summary>
		public static Exception Exception(string message = "", Exception innerException = null)
		{
			return new Exception(message, innerException);
		}

		/// <summary>
		/// It initializes a type of 'T' exception.
		/// </summary>
		public static T Exception<T>(string message, Exception innerException = null)
			where T : Exception
		{
			var constructor = typeof(T).GetConstructor(new[]
				                                           {
					                                           typeof(string), typeof(Exception)
				                                           });

			if (constructor.IsNull())
			{
				if (innerException.IsNull())
					constructor = typeof(T).GetConstructor(new[]
						                                       {
							                                       typeof(string)
						                                       });
				else
					ThrowException("Invalid exception constructor!");
			}

			return (T)constructor.Invoke(new object[]
				                             {
					                             message, innerException
				                             });
		}

		/// <summary>
		/// It intializes a type of 'T' exception. 
		/// </summary>
		public static T Exception<T>()
			where T : Exception, new()
		{
			return new T();
		}

		/// <summary>
		/// It returns null reference with "{0} of '{1}' cannot be null!" message,
		/// where {0} is 'name' parameter, {1} is type of 'obj'.
		/// </summary>
		public static NullReferenceException NullReference<T>(string name, T obj)
		{
			Ensure.NotBlank(name);
			return new NullReferenceException(string.Format("{0} of '{1}' cannot be null!", name, typeof(T).FullName));
		}

		/// <summary>
		/// It returns null reference with "{0} cannot be null!" message,
		/// where {0} is 'name' parameter.
		/// </summary>
		public static NullReferenceException NullReference(string name)
		{
			Ensure.NotBlank(name);
			return new NullReferenceException(string.Format("{0} cannot be null!", name));
		}

		#endregion

		#region Throw methods

		/// <summary>
		/// It throws a general exception with given message and inner exception if 'condition' is true
		/// </summary>
		public static void ThrowExceptionIf(bool condition, string message = "", Exception innerException = null)
		{
			if (condition)
				ThrowException(message, innerException);
		}

		/// <summary>
		/// It throws a general exception with given message and inner exception if 'condition' is true
		/// </summary>
		public static void ThrowExceptionIf(Func<bool> condition, string message = "", Exception innerException = null)
		{
			ThrowExceptionIf(condition(), message, innerException);
		}

		/// <summary>
		/// It throws a general exception with given message and inner exception.
		/// </summary>
		public static void ThrowException(string message = "", Exception innerException = null)
		{
			throw Exception(message, innerException);
		}

		/// <summary>
		/// It throws a general exception with given message and inner exception.
		/// </summary>
		public static void Throw<T>(string message, Exception innerException = null)
			where T : Exception
		{
			throw Exception<T>(message, innerException);
		}

		/// <summary>
		/// It throws a type of 'T' exception.
		/// </summary>
		public static void Throw<T>()
			where T : Exception, new()
		{
			throw Exception<T>();
		}

		/// <summary>
		/// It throws null reference exception.
		/// </summary>
		public static void ThrowNullReference(string name)
		{
			throw NullReference(name);
		}

		/// <summary>
		/// It throws null reference exception.
		/// </summary>
		public static void ThrowNullReference<T>(string name, T obj)
		{
			throw NullReference(name, obj);
		}

		#endregion
	}
}